﻿using Microsoft.Practices.Prism.Events;
using Neudesic.Pulse.Entities;
using Neudesic.Pulse.Events;
using Neudesic.Pulse.Infrastructure;
using Neudesic.Pulse.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Timers;

namespace Neudesic.Pulse.ViewModels
{
    /// <summary>
    /// View model for activity related to a user.
    /// </summary>
    [Export(typeof(ActivityListViewModel))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class ActivityListViewModel : ViewModel
    {

        Timer timer;
        string newer = string.Empty;

        /// <summary>
        /// Gets or sets the pulse.
        /// </summary>
        /// <value>The pulse.</value>
        private IPulseGateway Pulse { get; set; }

        private bool isBusy = false;

        /// <summary>
        /// Gets or sets the is busy.
        /// </summary>
        /// <value>The is busy.</value>
        public bool IsBusy
        {
            get { return isBusy; }
            set
            {
                if (!value.Equals(isBusy))
                {
                    isBusy = value;
                    this.RaisePropertyChanged(() => IsBusy);
                }
            }
        }

        /// <summary>
        /// Gets or sets the activities.
        /// </summary>
        /// <value>The activities.</value>
        public ObservableCollection<ActivityViewModel> Activities { get; private set; }

        /// <summary>
        /// Gets or sets the get activities command.
        /// </summary>
        /// <remarks>
        /// This was intended to be used both for code-initiated and for user-initiated routines (e.g., refresh button).  Due to time constraints, it's only being used now in code.
        /// </remarks>
        /// <value>The get activities command.</value>
        public AsyncDelegateCommand<PagedActivityList> GetActivitiesCommand { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ActivityListViewModel" /> class.
        /// </summary>
        /// <param name="pulse">The pulse.</param>
        [ImportingConstructor]
        public ActivityListViewModel(IEventAggregator eventAggregator, IPulseGateway pulse)
            : base(eventAggregator)
        {
            
            this.Pulse = pulse;

            // Create a new collection
            Activities = new ObservableCollection<ActivityViewModel>();

            GetActivitiesCommand = new AsyncDelegateCommand<PagedActivityList>(
                // execution
                () =>
                {
                    SetBusy(true);
                    return Pulse.GetActivities(newer);
                },
                // Can always execute
                null,
                // completion
                (t) =>
                {
                    if (t != null)
                    {
                        // upon completion, take newest bookmark and add items to collection
                        if (!string.IsNullOrEmpty(t.PagingBookmark.Newer))
                            this.newer = t.PagingBookmark.Newer;

                        if (t.Activities != null && t.Activities.Count > 0)
                        {

                            // Invoke on dispatcher so that UI is updated.
                            this.Dispatcher.Invoke(
                                (Action)(
                                    () =>
                                    {
                                        if (Activities.Count == 0)
                                        {
                                            // Add them wholesale, then exit
                                            t.Activities.ForEach((a) => Activities.Add(new ActivityViewModel(a, this.Pulse)));
                                            return;
                                        }

                                        // Otherwise, add the new items at the beginning of the activities
                                        t.Activities.FastReverse().ToList().ForEach((a) => Activities.Insert(0, new ActivityViewModel(a, this.Pulse))); // Add the items in reverses order to the front of the activity list.
                                    }
                                )
                                );
                        }
                    }
                    SetBusy(false);
                },
                (ex) =>
                {
                    if (ex != null)
                    {
                        EventAggregator.GetEvent<ApplicationExceptionEvent>().Publish(ex);
                    }
                    SetBusy(false);
                }
                );

            // Init timer and set properties
            timer = new Timer();
            timer.Interval = 30000; // TODO: Configuration service
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);

            // Fire the GetActivitiesCommand first
            GetActivitiesCommand.Execute(null);

        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            // Fire the GetActivitiesCommand
            GetActivitiesCommand.Execute(null);

        }

        private void SetBusy(bool isBusy)
        {

            // Set the timer appropriately
            timer.Enabled = !isBusy;

            // Set the IsBusy to false
            this.Dispatcher.Invoke(
                (Action)(
                () => { this.IsBusy = isBusy; }
                )
                );
        }

    }
}
